using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

namespace Toenda.Foundation.Data {
	/// <summary>
	/// Class DALSettings
	/// </summary>
	public class DALSettings {
		private string _connectionString;

		/// <summary>
		/// Initializes a new instance of the <see cref="DALSettings"/> class.
		/// </summary>
		public DALSettings() {
			this.ProviderType = ProviderType.MSSql2005;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="DALSettings"/> class.
		/// </summary>
		/// <param name="engine">The engine.</param>
		/// <param name="connectionString">The connection string.</param>
		public DALSettings(ProviderType engine, string connectionString) {
			this.ProviderType = engine;
			this._connectionString = connectionString;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="DALSettings"/> class.
		/// </summary>
		/// <param name="engine">The engine.</param>
		/// <param name="server">The server.</param>
		/// <param name="database">The database.</param>
		public DALSettings(ProviderType engine, string server, string database) {
			this.ProviderType = engine;
			this.Server = server;
			this.Database = database;
			this.IntegratedSecurity = true;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="DALSettings"/> class.
		/// </summary>
		/// <param name="engine">The engine.</param>
		/// <param name="server">The server.</param>
		/// <param name="database">The database.</param>
		/// <param name="userInstance">if set to <c>true</c> [user instance].</param>
		public DALSettings(ProviderType engine, string server, string database, bool userInstance) {
			this.ProviderType = engine;
			this.Server = server;
			this.Database = database;
			this.UserInstance = userInstance;
			this.IntegratedSecurity = true;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="DALSettings"/> class.
		/// </summary>
		/// <param name="engine">The engine.</param>
		/// <param name="user">The user.</param>
		/// <param name="password">The password.</param>
		/// <param name="server">The server.</param>
		/// <param name="database">The database.</param>
		public DALSettings(ProviderType engine, string user, string password, string server, string database) {
			this.ProviderType = engine;
			this.User = user;
			this.Password = password;
			this.Server = server;
			this.Database = database;
		}

		/// <summary>
		/// Get or set the failover partner
		/// </summary>
		public string FailoverPartner { get; set; }

		/// <summary>
		/// Get or set the engine
		/// </summary>
		public ProviderType ProviderType { get; set; }

		/// <summary>
		/// Get or set the user
		/// </summary>
		public string User { get; set; }

		/// <summary>
		/// Get or set the password
		/// </summary>
		public string Password { get; set; }

		/// <summary>
		/// Get or set the server
		/// </summary>
		public string Server { get; set; }

		/// <summary>
		/// Get or set the port
		/// </summary>
		public string Port { get; set; }

		/// <summary>
		/// Get or set the Database
		/// </summary>
		public string Database { get; set; }

		/// <summary>
		/// Get Trusted Connection
		/// </summary>
		public bool TrustedConnection { get; set; }

		/// <summary>
		/// Get persist security info
		/// </summary>
		public bool PersistSecurityInfo { get; set; }

		/// <summary>
		/// Get integrated security
		/// </summary>
		public bool IntegratedSecurity { get; set; }

		/// <summary>
		/// Get MultipleActiveResultSets
		/// </summary>
		public bool MultipleActiveResultSets { get; set; }

		/// <summary>
		/// Get user instance
		/// </summary>
		public bool UserInstance { get; set; }

		/// <summary>
		/// Get or set the name of the primary key
		/// </summary>
		public string PrimaryKeyName { get; set; }

		/// <summary>
		/// Gets the name of the provider.
		/// </summary>
		/// <value>The name of the provider.</value>
		public string ProviderName {
			get { return ConnectionStringGenerator.ConvertProviderTypeToString(this.ProviderType); }
		}

		/// <summary>
		/// Get a value that indicates that this connectionstring is for a windows authentication
		/// </summary>
		public bool IsWindowsAuthentication {
			get {
				if(this.User.IsNullOrTrimmedEmpty()
				&& this.Password.IsNullOrTrimmedEmpty()) {
					return true;
				}
				else {
					return false;
				}
			}
		}

		/// <summary>
		/// Get or set the complete connection string
		/// </summary>
		public string ConnectionString {
			get {
				if(this._connectionString.IsNullOrTrimmedEmpty()) {
					StringBuilder str = new StringBuilder();
					ConnectionStringGenerator generator = new ConnectionStringGenerator(this);

					switch(this.ProviderType) {
						case ProviderType.MySql:
							//Data Source = myServerAddress;
							//Database = myDataBase;
							//User ID = myUsername;
							//Password = myPassword;
							//Command Logging = false;

							str.Append(generator.GetServerString(false));
							str.Append(generator.UserString);
							str.Append(generator.PasswordString);
							str.Append(generator.GetDatabaseString());
							break;

						case ProviderType.Access:
							//Provider=Microsoft.ACE.OLEDB.12.0;
							//Data Source=C:\myFolder\myAccess2007file.accdb;
							//Jet OLEDB:Database Password=MyDbPassword;
							break;

						case ProviderType.Odbc:
							break;

						case ProviderType.OleDb:
							break;

						case ProviderType.MSSql2000:
						case ProviderType.MSSql2005:
						case ProviderType.MSSql2008:
						default:
							str.Append(generator.GetServerString());
							str.Append(generator.UserString);
							str.Append(generator.PasswordString);
							str.Append(generator.GetDatabaseString());
							str.Append(generator.TrustedConnectionString);
							str.Append(generator.PersistSecurityInfoString);
							str.Append(generator.IntegratedSecurityString);
							str.Append(generator.UserInstanceString);
							str.Append(generator.MultipleActiveResultSetsString);
							str.Append(generator.FailoverPartnerString);
							break;
					}

					this._connectionString = str.ToString();
				}

				return this._connectionString;
			}
			set { this._connectionString = value; }
		}

		/// <summary>
		/// Read connection information from configuration file.
		/// </summary>
		/// <param name="name">Name of the connection string to read.</param>
		/// <returns>An instance of Toenda.Foundation.Data.DALSettings containing the configured connection settings.</returns>
		public static DALSettings FromConfiguration(string name) {
			ConnectionStringSettings conStringSettings = ConfigurationManager.ConnectionStrings[name];

			if(conStringSettings == null) {
				throw new KeyNotFoundException("The requested connection string was not found in the configuration file.");
			}

			DALSettings settings = UdlParser.ParseConnectionString(conStringSettings.ConnectionString);

			if(!conStringSettings.ProviderName.IsNullOrTrimmedEmpty()) {
				settings.ProviderType = ConnectionStringGenerator.ConvertStringToProviderType(conStringSettings.ProviderName);
			}
			else {
				if(settings.ProviderType == ProviderType.Unknown) {
					settings.ProviderType = ConnectionStringGenerator.ConvertStringToProviderType(settings.ProviderName);
				}
			}

			return settings;
		}

		/// <summary>
		/// Implements a internal connection string generator class
		/// </summary>
		internal class ConnectionStringGenerator {
			private DALSettings _settings;

			/// <summary>
			/// Creates a new instance of <see cref="ConnectionStringGenerator"/>
			/// </summary>
			/// <param name="settings"></param>
			public ConnectionStringGenerator(DALSettings settings) {
				this._settings = settings;
			}

			/// <summary>
			/// Converts a provider type to a string
			/// </summary>
			/// <param name="providerType"></param>
			/// <returns></returns>
			public static string ConvertProviderTypeToString(ProviderType providerType) {
				switch(providerType) {
					case ProviderType.Access:
					case ProviderType.OleDb:
						return "System.Data.OleDb";

					case ProviderType.MySql:
						return "MySql.Data.MySqlClient";

					case ProviderType.Odbc:
						return "System.Data.Odbc";

					case ProviderType.SQLite:
						return "System.Data.SQLite";

					case ProviderType.Oracle:
						/* There are two providers available:
						 * - Oracle.DataAccess.Client (Oracle)
						 * - System.Data.OracleClient (Microsoft) */
						return "System.Data.OracleClient";

					case ProviderType.MSSql2000:
					case ProviderType.MSSql2005:
					case ProviderType.MSSql2008:
					default:
						return "System.Data.SqlClient";
				}
			}

			/// <summary>
			/// Converts a string to a provider type
			/// </summary>
			/// <param name="providerType"></param>
			/// <returns></returns>
			public static ProviderType ConvertStringToProviderType(string providerType) {
				if(!providerType.IsNullOrTrimmedEmpty()) {
					switch(providerType) {
						case "System.Data.OleDb":
							return ProviderType.OleDb;

						case "System.Data.Odbc":
							return ProviderType.Odbc;

						case "Oracle.DataAccess.Client":
						case "System.Data.OracleClient":
							return ProviderType.Oracle;

						case "System.Data.SqlClient":
							//return ProviderType.MSSql2005;
							//return ProviderType.MSSql2000;
							return ProviderType.MSSql2008;

						case "System.Data.SQLite":
							return ProviderType.SQLite;

						default:
							throw new NotSupportedException("Provider type not supported.");
					}
				}
				else {
					throw new NotSupportedException("Provider type not supported.");
				}
			}

			/// <summary>
			/// Get server string
			/// </summary>
			/// <returns></returns>
			public string GetServerString() {
				return GetServerString(true);
			}

			/// <summary>
			/// Get server string
			/// </summary>
			/// <param name="msSpecific">If the string should be Microsoft specific, this must be true</param>
			public string GetServerString(bool msSpecific) {
				if(!this._settings.Server.IsNullOrTrimmedEmpty()) {
					if(msSpecific) {
						return "Server=" + this._settings.Server + "; ";
					}
					else {
						return "Data Source=" + this._settings.Server + "; ";
					}
				}
				else {
					return "";
				}
			}

			/// <summary>
			/// Get database string
			/// </summary>
			/// <returns></returns>
			public string GetDatabaseString() {
				return GetDatabaseString(false);
			}

			/// <summary>
			/// Get database string
			/// </summary>
			/// <param name="compatible">If the string should be compatible, this must be true</param>
			public string GetDatabaseString(bool compatible) {
				if(!this._settings.Database.IsNullOrTrimmedEmpty()) {
					if(compatible) {
						return "Initial Catalog=" + this._settings.Database + "; ";
					}
					else {
						return "Database=" + this._settings.Database + "; ";
					}
				}
				else {
					return "";
				}
			}

			/// <summary>
			/// Get user string
			/// </summary>
			public string UserString {
				get {
					if(!this._settings.User.IsNullOrTrimmedEmpty()) {
						return "User ID=" + this._settings.User + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get password string
			/// </summary>
			public string PasswordString {
				get {
					if(!this._settings.Password.IsNullOrTrimmedEmpty()) {
						return "Password=" + this._settings.Password + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get failover partner string
			/// </summary>
			public string FailoverPartnerString {
				get {
					if(!this._settings.FailoverPartner.IsNullOrTrimmedEmpty()) {
						return "Failover Partner=" + this._settings.FailoverPartner + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get trusted connection string
			/// </summary>
			public string TrustedConnectionString {
				get {
					if(this._settings.TrustedConnection) {
						return "Trusted_Connection=" + this._settings.TrustedConnection.ToString() + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get persist security info string
			/// </summary>
			public string PersistSecurityInfoString {
				get {
					if(this._settings.PersistSecurityInfo) {
						return "Persist Security Info=" + this._settings.PersistSecurityInfo.ToString() + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get integrated security string
			/// </summary>
			public string IntegratedSecurityString {
				get {
					if(this._settings.IntegratedSecurity) {
						return "Integrated Security=" + this._settings.IntegratedSecurity.ToString() + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get user instance string
			/// </summary>
			public string UserInstanceString {
				get {
					if(this._settings.UserInstance) {
						return "User Instance=" + this._settings.UserInstance.ToString() + "; ";
					}
					else {
						return "";
					}
				}
			}

			/// <summary>
			/// Get MultipleActiveResultSets string
			/// </summary>
			public string MultipleActiveResultSetsString {
				get {
					if(this._settings.MultipleActiveResultSets) {
						return "MultipleActiveResultSets=" + this._settings.MultipleActiveResultSets.ToString() + "; ";
					}
					else {
						return "";
					}
				}
			}
		}
	}
}
